package io.hepu.robotize.config;

import io.hepu.robotize.cache.ShiroCacheManager;
import io.hepu.robotize.model.Permission;
import io.hepu.robotize.security.JwtAuthcFilter;
import io.hepu.robotize.security.JwtAuthzFilter;
import io.hepu.robotize.security.ShiroJdbcRealm;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfiguration {
    private static final Logger LOG = LoggerFactory.getLogger(ShiroConfiguration.class);

    @Resource
    private ShiroProperties properties;

    // + -------------------------
    // + 验证数据源
    // + -------------------------
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    @ConditionalOnMissingBean(ShiroJdbcRealm.class)
    public ShiroJdbcRealm shiroJdbcRealm() {
        return new ShiroJdbcRealm();
    }

    // + -------------------------
    // + Shiro核心安全管理器
    // + -------------------------
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 通过Reaml来实现验证
        securityManager.setRealm(shiroJdbcRealm());

        // 关闭自带session
        DefaultSessionStorageEvaluator evaluator = new DefaultSessionStorageEvaluator();
        evaluator.setSessionStorageEnabled(false);
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        subjectDAO.setSessionStorageEvaluator(evaluator);
        securityManager.setSubjectDAO(subjectDAO);

        // 设置Redis作为缓存管理
        securityManager.setCacheManager(new ShiroCacheManager());
        return securityManager;
    }

    @Bean(name = "filterShiroFilterRegistrationBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean() throws Exception {
        LOG.info("<----------- Initialize FilterShiroFilterRegistrationBean ----------->");

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全管理接口{必须}
        shiroFilterFactoryBean.setSecurityManager(securityManager());

        // 添加过滤器
        Map<String, Filter> filterMap = new LinkedHashMap<>(8);
        filterMap.put("jwtAuthc", new JwtAuthcFilter());
        filterMap.put("jwtAuthz", new JwtAuthzFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        Map<String, String> chainMap = shiroFilterChainDefinition().getFilterChainMap();
        shiroFilterFactoryBean.setFilterChainDefinitionMap(chainMap);

        return shiroFilterFactoryBean;
    }

    /**
     * 配置过滤器链
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() throws Exception {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        // 以下请求不通过JWT Filter校验
        for (String path : properties.getWhitelist()) {
            chainDefinition.addPathDefinition(path, "anon");
        }

        // 过滤器中注册所有的权限
        for (Permission perm : properties.getPermissions()) {
            chainDefinition.addPathDefinition(perm.getUri(), "jwtAuthc,jwtAuthz[" + perm.getCode() + "]");
        }

        // 拦截所有请求
        chainDefinition.addPathDefinition("/**", "jwtAuthc,jwtAuthz");
        LOG.info("-------> ::: ShiroFilterChainDefinition: {}", chainDefinition.getFilterChainMap());
        return chainDefinition;
    }

    // + ---------------------------------------------------------------
    // + 开启Shiro的注解(如 @RequiresRoles,@RequiresPermissions)
    // +
    // + 需要配置Bean:
    // + DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor
    // + ----------------------------------------------------------------

    // DefaultAdvisorAutoProxyCreator是用来扫描上下文,寻找所有的Advistor(通知器)
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        /*
         * 解决重复代理问题 github#994
         * 添加前缀判断 不匹配 任何Advisor
         */
        advisorAutoProxyCreator.setUsePrefix(true);
        advisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor");
        return advisorAutoProxyCreator;
    }

    // + -------------------------
    // + 开启Shiro AOP注解权限支持
    // + -------------------------
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    // + ------------------------------------------
    // + LifecycleBeanPostProcessor
    // + key {@code static} make @Value effective
    // + ------------------------------------------
    @Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
}
